#ifndef CORE_H
# include "..\..\inc\System\Core.h"
#endif
#include "ConvertEx.hpp"

namespace Structs {

	#define ALWAYS(X) (X)
	#define NEVER(X) (X)
	#define SLOT_0_2_0 0x001fc07f
	#define SLOT_4_2_0 0xf01fc07f
	#define MAX_U32 (uint)((((ulong)1) << 32) - 1)
	
	byte ConvertEx::_getVariant9L(byte p[], ulong &v)
	{
		uint a, b, s;
		a = *p;
		// a: p0 (unmasked)
		if (!(a & 0x80)) { v = a; return 1; }
		b = *++p;
		// b: p1 (unmasked)
		if (!(b & 0x80))
		{
			a &= 0x7f;
			a = a << 7;
			a |= b;
			v = a;
			return 2;
		}
		// Verify that constants are precomputed correctly
		Debug_Assert(SLOT_0_2_0 == ((0x7f << 14) | 0x7f));
		Debug_Assert(SLOT_4_2_0 == ((0xfU << 28) | (0x7f << 14) | 0x7f));
		a = a << 14;
		a |= *++p;
		// a: p0<<14 | p2 (unmasked)
		if (!(a & 0x80))
		{
			a &= SLOT_0_2_0;
			b &= 0x7f;
			b = b << 7;
			a |= b;
			v = a;
			return 3;
		}
		// CSE1 from below
		a &= SLOT_0_2_0;
		b = b << 14;
		b |= *++p;
		// b: p1<<14 | p3 (unmasked)
		if (!(b&0x80))
		{
			b &= SLOT_0_2_0;
			// moved CSE1 up
			// a &= (0x7f<<14)|(0x7f); */
			a = a << 7;
			a |= b;
			v = a;
			return 4;
		}
		// a: p0<<14 | p2 (masked)
		// b: p1<<14 | p3 (unmasked)
		// 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked)
		// moved CSE1 up
		// a &= (0x7f<<14)|(0x7f);
		b &= SLOT_0_2_0;
		s = a;
		// s: p0<<14 | p2 (masked)
		a = a << 14;
		a |= *++p;
		// a: p0<<28 | p2<<14 | p4 (unmasked)
		if (!(a&0x80))
		{
			/* we can skip these cause they were (effectively) done above in calc'ing s */
			/* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
			/* b &= (0x7f<<14)|(0x7f); */
			b = b << 7;
			a |= b;
			s = s >> 18;
			v = ((ulong)s) << 32 | a;
			return 5;
		}
		// 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked)
		s = s << 7;
		s |= b;
		// s: p0<<21 | p1<<14 | p2<<7 | p3 (masked)
		b = b << 14;
		b |= *++p;
		// b: p1<<28 | p3<<14 | p5 (unmasked)
		if (!(b & 0x80))
		{
			// we can skip this cause it was (effectively) done above in calc'ing s
			// b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
			a &= SLOT_0_2_0;
			a = a << 7;
			a |= b;
			s = s >> 18;
			v = ((ulong)s) << 32 | a;
			return 6;
		}
		a = a << 14;
		a |= *++p;
		// a: p2<<28 | p4<<14 | p6 (unmasked)
		if (!(a & 0x80))
		{
			a &= SLOT_4_2_0;
			b &= SLOT_0_2_0;
			b = b<<7;
			a |= b;
			s = s>>11;
			v = ((ulong)s)<<32 | a;
			return 7;
		}
		// CSE2 from below
		a &= SLOT_0_2_0;
		b = b << 14;
		b |= *++p;
		// b: p3<<28 | p5<<14 | p7 (unmasked)
		if (!(b & 0x80))
		{
			b &= SLOT_4_2_0;
			// moved CSE2 up
			// a &= (0x7f<<14)|(0x7f);
			a = a << 7;
			a |= b;
			s = s >> 4;
			v = ((ulong)s) << 32 | a;
			return 8;
		}
		a = a << 15;
		a |= *++p;
		// a: p4<<29 | p6<<15 | p8 (unmasked)
		// moved CSE2 up
		// a &= (0x7f<<29)|(0x7f<<15)|(0xff);
		b &= SLOT_0_2_0;
		b = b << 8;
		a |= b;
		s = s << 4;
		b = p[-4];
		b &= 0x7f;
		b = b >> 3;
		s |= b;
		v = ((ulong)s) << 32 | a;
		return 9;
	}
		
	byte ConvertEx::_getVaraint4(byte p[], uint &v)
	{
		uint a, b;
        // The 1-byte case.  Overwhelmingly the most common.  Handled inline  by the getVarin32() macro
        a = *p;
        // a: p0 (unmasked)
		// The 2-byte case
		b = *++p;
        // b: p1 (unmasked)
        if (!(b & 0x80))
        {
            // Values between 128 and 16383
            a &= 0x7f;
            a = a << 7;
            v = a | b;
            return 2;
        }
		// The 3-byte case
		a = a << 14;
		a |= *++p;
		// a: p0<<14 | p2 (unmasked)
		if (!(a & 0x80))
		{
			// Values between 16384 and 2097151
			a &= (0x7f << 14) | 0x7f;
			b &= 0x7f;
			b = b << 7;
			v = a | b;
			return 3;
		}
		// A 32-bit varint is used to store size information in btrees. Objects are rarely larger than 2MiB limit of a 3-byte varint.
        // A 3-byte varint is sufficient, for example, to record the size of a 1048569-byte BLOB or string.
        // We only unroll the first 1-, 2-, and 3- byte cases.  The very rare larger cases can be handled by the slower 64-bit varint
        // routine.
		{
			ulong v64;
			p -= 2;
			byte n = _getVariant9L(p, v64);
			Debug_Assert(n > 3 && n <= 9);
			v = ((v64 & MAX_U32) != v64 ? 0xffffffff : (uint)v64);
			return n;
		}
	}

	byte ConvertEx::GetVariantLength(ulong v)
    {
        byte i = 0;
        do { i++; v >>= 7; }
        while (v != 0 && ALWAYS(i < 9));
        return i;
    }

	byte ConvertEx::PutVariant9L(byte p[], ulong v)
    {
        int i, j; byte n;
        if ((v & (((ulong)0xff000000) << 32)) != 0)
        {
            p[8] = (byte)v;
            v >>= 8;
            for (i = 7; i >= 0; i--)
            {
                p[i] = (byte)((v & 0x7f) | 0x80);
                v >>= 7;
            }
            return 9;
        }
        n = 0;
        byte b[10];
        do
        {
            b[n++] = (byte)((v & 0x7f) | 0x80);
            v >>= 7;
        } while (v != 0);
        b[0] &= 0x7f;
        Debug_Assert(n <= 9);
        for (i = 0, j = n - 1; j >= 0; j--, i++)
            p[i] = b[j];
        return n;
    }
}
